package org.apache.ocean.name;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.builder.EqualsBuilder;
import org.apache.ocean.CElement;
import org.apache.ocean.XMLUtil;
import org.jdom.Element;

/**
 * 
 * @author jasonr
 */
public class NamesInfosGroup implements CElement {
	Map<String, ReportedNameInfos> nameInfosMap = new HashMap<String, ReportedNameInfos>();

	public NamesInfosGroup(Element root) throws Exception {
		nameInfosMap = new HashMap<String, ReportedNameInfos>();
		for (Element element : XMLUtil.getChildren("reportednameinfos", root)) {
			ReportedNameInfos reportedNameInfos = new ReportedNameInfos(element);
			nameInfosMap.put(reportedNameInfos.fromNameID, reportedNameInfos);
		}
	}

	// TODO: test out multiple name servers
	public NameInfo getChosenMasterNameInfo() {
		if (allMatching()) {
			NameInfo chosenNameInfo = null;
			for (ReportedNameInfos reportedNameInfos : nameInfosMap.values()) {
				NameInfo ni = reportedNameInfos.chooseMasterNameInfo();
				if (chosenNameInfo == null)
					chosenNameInfo = ni;
				if (chosenNameInfo != null) {
					if (!StringUtils.equals(chosenNameInfo.id, ni.id)) {
						return null;
					} else {
						chosenNameInfo = ni;
					}
				}
			}
			return chosenNameInfo;
		} else {
			return null;
		}
	}

	public NameInfo getNameInfoFromRealServer(String id) {
		if (id != null) {
			Iterator<ReportedNameInfos> iterator = nameInfosMap.values().iterator();
			if (iterator.hasNext()) {
				ReportedNameInfos reportedNameInfos = iterator.next();
				if (StringUtils.equals(reportedNameInfos.fromNameID, id)) {
					for (NameInfo n : reportedNameInfos.nameInfos) {
						if (StringUtils.equals(n.id, id)) {
							return n;
						}
					}
				}
			}
		}
		return null;
	}

	/**
	 * public String getMasterNameID() { Iterator<ReportedNameInfos> iterator =
	 * nameInfosMap.values().iterator(); if (iterator.hasNext()) {
	 * ReportedNameInfos reportedNameInfos = iterator.next(); if
	 * (reportedNameInfos.nameInfos.size() > 0) { return
	 * reportedNameInfos.nameInfos.get(0).masterNameID; } } return null; }
	 */
	public void add(ReportedNameInfos reportedNameInfos) {
		nameInfosMap.put(reportedNameInfos.fromNameID, reportedNameInfos);
	}

	public Element toElement() throws Exception {
		Element root = new Element("nameinfosgroup");
		if (nameInfosMap != null) {
			for (ReportedNameInfos reportedNameInfos : nameInfosMap.values()) {
				root.addContent(reportedNameInfos.toElement());
			}
		}
		return root;
	}

	public NamesInfosGroup() {
	}

	public boolean allMatching() {
		if (nameInfosMap.size() == 1) {
			return true;
		}
		List<List<NameInfo>> list = new ArrayList<List<NameInfo>>(nameInfosMap.size());
		for (ReportedNameInfos reportedNameInfos : nameInfosMap.values()) {
			Collections.sort(reportedNameInfos.nameInfos, new NameInfoIDComparator());
			list.add(reportedNameInfos.nameInfos);
		}
		Iterator<List<NameInfo>> iterator = list.iterator();
		if (iterator.hasNext()) {
			List<NameInfo> first = iterator.next();
			while (iterator.hasNext()) {
				List<NameInfo> other = iterator.next();
				if (other.size() != first.size()) {
					return false;
				}
				if (!same(first, other)) {
					return false;
				}
			}
		} else {
			return false;
		}
		return true;
	}

	private boolean same(List<NameInfo> o1, List<NameInfo> o2) {
		Iterator<NameInfo> iterator1 = o1.iterator();
		Iterator<NameInfo> iterator2 = o2.iterator();
		while (iterator1.hasNext()) {
			NameInfo nameInfo1 = iterator1.next();
			NameInfo nameInfo2 = iterator2.next();
			if (!same(nameInfo1, nameInfo2)) {
				return false;
			}
		}
		return true;
	}

	private boolean same(NameInfo o1, NameInfo o2) {
		EqualsBuilder equalsBuilder = new EqualsBuilder();
		equalsBuilder.append(o1.id, o2.id);
		equalsBuilder.append(o1.masterNameID, o2.masterNameID);
		equalsBuilder.append(o1.highestReplicationIDDatabase, o2.highestReplicationIDDatabase);
		return equalsBuilder.isEquals();
	}

	public static class NameInfoIDComparator implements Comparator<NameInfo> {
		public int compare(NameInfo o1, NameInfo o2) {
			return o1.id.compareTo(o2.id);
		}
	}
}
